/*
 * @Author       : Shandong Xiedali
 * @Mail         : 9727005@qq.com
 * @Date         : 2024/12/24 10:15:51
 * @FilePath     : typeHelper.ts
 * @Description  :
 * Copyright (c) 2024 by Hiland & RainyTop, All Rights Reserved.
 */


import {Convertor} from "./convertor";

/**
 * 因为不是所有的类型都继承与Object,所有对目标对象类型的判断,
 * 不能通过原型中注入的方式实现.因此没有typeHelper对应的Injector.
 */
export class TypeHelper {
    /**
     * 所有类型的字符串表示
     */
    static ObjectTypes = {
        null     : "null",
        string   : "string",
        date     : "date",
        boolean  : "boolean",
        undefined: "undefined",
        function : "function",
        number   : "number",
        array    : "array",
        symbol   : "symbol",
        error    : "error",
        regexp   : "regexp",
        object   : "object",
    };

    /**
     * 判断数据的数据类型
     * @param data
     * @returns {string}
     */
    static getType = function (data: any): string {
        let typeName: string = typeof (data);

        /**
         * 对undefined类型要先判断
         */
        if (typeName === "undefined") {
            return TypeHelper.ObjectTypes.undefined;
        }

        if (data == null) {
            return TypeHelper.ObjectTypes.null;
        }

        if (typeName === "object") {
            // 因为各个类型的toString()方法都被重写过,
            // 因此需要使用最原始的Object.prototype.toString.call()方法才能获取到对象的类型
            const typeObject = Object.prototype.toString.call(data);

            switch (typeObject) {
                case "[object Null]":
                    typeName = TypeHelper.ObjectTypes.null;
                    break;
                case "[object Array]":
                    typeName = TypeHelper.ObjectTypes.array;
                    break;
                case "[object Date]":
                    typeName = TypeHelper.ObjectTypes.date;
                    break;
                case "[object RegExp]":
                    typeName = TypeHelper.ObjectTypes.regexp;
                    break;
                case "[object Error]":
                    typeName = TypeHelper.ObjectTypes.error;
                    break;
                default:
                    typeName = TypeHelper.ObjectTypes.object;
            }
        }

        return typeName;
    };

    /**
     *
     * @param data
     * @returns {boolean}
     */
    static isNumber = function (data: any): boolean {
        return TypeHelper.getType(data) === TypeHelper.ObjectTypes.number;
    };

    /**
     *
     * @param data
     * @returns {boolean}
     */
    static isString = function (data: any): boolean {
        return TypeHelper.getType(data) === TypeHelper.ObjectTypes.string;
    };

    /**
     *
     * @param data
     * @returns {boolean}
     */
    static isArray = function (data: any): boolean {
        return TypeHelper.getType(data) === TypeHelper.ObjectTypes.array;
    };

    /**
     *
     * @param data
     * @returns {boolean}
     */
    static isBoolean = function (data: any): boolean {
        return TypeHelper.getType(data) === TypeHelper.ObjectTypes.boolean;
    };

    /**
     *
     * @param data
     * @returns {boolean}
     */
    static isUndefined = function (data: any): boolean {
        return TypeHelper.getType(data) === TypeHelper.ObjectTypes.undefined;
    };

    /**
     *
     * @param data
     * @returns {boolean}
     */
    static isNull = function (data: any): boolean {
        return TypeHelper.getType(data) === TypeHelper.ObjectTypes.null;
    };

    /**
     *
     * @param data
     * @returns {boolean}
     */
    static isSymbol = function (data: any): boolean {
        return TypeHelper.getType(data) === TypeHelper.ObjectTypes.symbol;
    };

    /**
     *
     * @param data
     * @returns {boolean}
     */
    static isObject = function (data: any): boolean {
        return TypeHelper.getType(data) === TypeHelper.ObjectTypes.object;
    };

    /**
     *
     * @param data
     * @returns {boolean}
     */
    static isFunction = function (data: any): boolean {
        return TypeHelper.getType(data) === TypeHelper.ObjectTypes.function;
    };

    /**
     * 判断给定的数据是否为引用类型
     * @param value
     * @return {boolean}
     */
    static isReferenceType = function (value: any): boolean {
        return value instanceof Object;
    }

    /**
     * 判断给定的数据是否为值类型
     * 特别说明：
     * 1. null, undefined,符串，数字，布尔值，都是值类型
     * 2. 字数组,日期,正则表达式,错误,对象,函数都是引用类型
     * @param value
     * @return {boolean}
     */
    static isValueType = function (value: any): boolean {
        return !TypeHelper.isReferenceType(value);
    }

    /**
     * 判断一个类是否是另一个类的子类
     * @param subclass
     * @param superclass
     * @returns {boolean}
     * @example
     * isSubclass(Worker, Human) // true
     */
    static isSubclass<SUB, SUP>(subclass: new (...args: any[]) => SUB, superclass: new (...args: any[]) => SUP): boolean {
        // 检查 subclass 的原型是否为 superclass 的实例
        return subclass.prototype instanceof superclass;
    }

    /**
     * 类型转换（目前仅能转换简单类型，如string、number、boolean、Date等）
     * @template IN 输入数据的类型。
     * @template OUT 输出数据的类型。
     * @param {IN} inputData 输入数据
     * @param {OUT} defaultValue 默认值
     * @return {OUT} 转换后的数据
     */
    static convert<IN, OUT>(inputData: IN, defaultValue: OUT): OUT {
        return Convertor.changeType(inputData, defaultValue);
    }

    /**
     * 类型断言（假定你知道输入数据的类型，把IN类型当做为OUT类型使用；但inputData的类型没有改变，其依然是IN类型）
     * @template IN 输入数据的类型。
     * @template OUT 输出数据的类型。
     * @param {IN} inputData
     * @return {OUT} 转换后的数据
     */
    static as<IN, OUT>(inputData: IN): OUT {
        return Convertor.asType<IN, OUT>(inputData);
    }
}
